use crate::{common, tables::api::TbApi};
use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
use once_cell::sync::Lazy;
use rbatis::rbdc;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::{collections::HashMap, sync::RwLock};

#[derive(Default)]
struct ApiCache {
    id_api: HashMap<u64, TbApi>,
    path_id: HashMap<String, u64>,
}

static API_CACHE: Lazy<RwLock<ApiCache>> = Lazy::new(|| RwLock::new(ApiCache::default()));

pub async fn init_apis() -> Result<(), rbdc::Error> {
    let apis = TbApi::get_all_api().await?;

    let api_cache = &mut API_CACHE.write().unwrap();
    apis.into_iter().for_each(|a| {
        let id = a.id.unwrap();
        let path = a.method.clone() + "@" + &a.path;
        api_cache.path_id.insert(path, id);
        api_cache.id_api.insert(id, a);
    });

    Ok(())
}

async fn insert_api(data: &mut TbApi) -> Result<(), rbdc::Error> {
    TbApi::insert_api(data).await?;

    let api_cache = &mut API_CACHE.write().unwrap();
    let id = data.id.unwrap();
    let path = data.method.clone() + "@" + &data.path;
    api_cache.path_id.insert(path, id);
    api_cache.id_api.insert(id, data.to_owned());

    Ok(())
}

async fn update_api(data: &TbApi) -> Result<(), rbdc::Error> {
    TbApi::update_api(data).await?;

    let api_cache = &mut API_CACHE.write().unwrap();
    let id = data.id.unwrap();
    let a = api_cache.id_api.get(&id);
    if let Some(a) = a {
        let old_path = a.method.clone() + "@" + &a.path;
        api_cache.path_id.remove(&old_path);
    }
    let path = data.method.clone() + "@" + &data.path;
    api_cache.path_id.insert(path, id);
    api_cache.id_api.insert(id, data.to_owned());

    Ok(())
}

async fn delete_api(id: u64) -> Result<(), rbdc::Error> {
    TbApi::delete_api(id).await?;

    let api_cache = &mut API_CACHE.write().unwrap();
    let a = api_cache.id_api.get(&id);
    if let Some(a) = a {
        let old_path = a.method.clone() + "@" + &a.path;
        api_cache.path_id.remove(&old_path);
    }
    api_cache.id_api.remove(&id);

    Ok(())
}

pub fn get_api_id(method: &str, path: &String) -> Option<u64> {
    let path = method.to_owned() + "@" + path;

    let api_cache = API_CACHE.read().unwrap();
    api_cache.path_id.get(&path).copied()
}

#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Get {
    path: Option<String>,
    description: Option<String>,
    apigroup: Option<String>,
    method: Option<String>,
}

#[get("")]
async fn get(page: web::Query<common::QueryPage>, query: web::Query<Get>) -> impl Responder {
    common::ResData::<TbApi>::from_page(TbApi::get_apis(&page, &query).await).resp_json()
}

fn check_api(data: &web::Json<TbApi>) -> Result<(), &str> {
    if data.path.is_empty() {
        return Err("API路径不能为空");
    }

    if data.description.is_empty() {
        return Err("API描述不能为空");
    }

    if data.apigroup.is_empty() {
        return Err("API组不能为空");
    }

    if data.method.is_empty() {
        return Err("API方法不能为空");
    }

    Ok(())
}

#[post("")]
async fn post(mut data: web::Json<TbApi>) -> impl Responder {
    let check = check_api(&data);
    if let Err(e) = check {
        return HttpResponse::Ok().json(json!({
            "success": false,
            "errorMessage": e
        }));
    }

    HttpResponse::Ok().json(match insert_api(&mut data).await {
        Ok(_) => json!({
            "success": true,
            "errorMessage": "操作成功"
        }),
        Err(e) => json!({
            "success": false,
            "errorMessage": e.to_string().as_str()
        }),
    })
}

#[put("")]
async fn put(data: web::Json<TbApi>) -> impl Responder {
    let check = check_api(&data);
    if let Err(e) = check {
        return HttpResponse::Ok().json(json!({
            "success": false,
            "errorMessage": e
        }));
    }

    HttpResponse::Ok().json(match update_api(&data).await {
        Ok(_) => json!({
            "success": true,
            "errorMessage": "操作成功"
        }),
        Err(e) => json!({
            "success": false,
            "errorMessage": e.to_string().as_str()
        }),
    })
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Delete {
    pub id: u64,
}

#[delete("")]
async fn delete(data: web::Json<Delete>) -> impl Responder {
    HttpResponse::Ok().json(match delete_api(data.id).await {
        Ok(_) => json!({
            "success": true,
            "errorMessage": "操作成功"
        }),
        Err(e) => json!({
            "success": false,
            "errorMessage": e.to_string().as_str()
        }),
    })
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Api {
    id: Option<i8>,
    description: String,
    children: Vec<TbApi>,
}

#[get("/all")]
async fn get_all() -> impl Responder {
    let api_cache = API_CACHE.read().unwrap();

    let group_apis = &mut HashMap::<String, Vec<TbApi>>::default();

    api_cache.id_api.iter().for_each(|(_, v)| {
        if !group_apis.contains_key(&v.apigroup) {
            group_apis.insert(v.apigroup.clone(), vec![]);
        }
        group_apis.get_mut(&v.apigroup).unwrap().push(v.to_owned());
    });

    let apis = &mut vec![];

    let mut id = 0;
    group_apis.iter().for_each(|(k, v)| {
        apis.push(Api {
            id: Some(id),
            description: k.into(),
            children: v.to_vec(),
        });
        id -= 1;
    });

    apis.sort_by(|a, b| a.description.cmp(&b.description));

    HttpResponse::Ok().json(apis)
}

pub fn routes(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::scope("/api")
            .service(get_all)
            .service(get)
            .service(post)
            .service(put)
            .service(delete),
    );
}

#[cfg(test)]
mod tests {
    use crate::{common, handlers::api, utils::mysql};
    use actix_web::{test, App};
    use serde_json::{json, Value};

    use super::Get;

    #[actix_web::test]
    async fn test_get_all_api() {
        mysql::init_pool().await;

        super::init_apis().await.expect("init apis error");

        let app = test::init_service(App::new().configure(api::routes)).await;

        let req = test::TestRequest::get().uri("/api/all").to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status: {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body: {:#?}", json_value);
        assert!(status.is_success());
    }

    #[actix_web::test]
    async fn test_get_apis() {
        mysql::init_pool().await;

        let app = test::init_service(App::new().configure(api::routes)).await;

        let page = &mut common::QueryPage::default();
        page.page_size = Some(5);
        let page = serde_urlencoded::to_string(page).unwrap();
        let query = &mut Get::default();
        query.method = Some("GET".into());
        let query = serde_urlencoded::to_string(query).unwrap();

        let req = test::TestRequest::get()
            .uri(&("/api?".to_owned() + page.as_str() + "&" + query.as_str()))
            .to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status: {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body: {:#?}", json_value);
        assert!(status.is_success());
    }

    #[actix_web::test]
    async fn test_add_api() {
        mysql::init_pool().await;

        super::init_apis().await.expect("init apis error");

        let app = test::init_service(App::new().configure(api::routes)).await;

        let req = test::TestRequest::post().uri("/api").set_json(json!({"path": "/api/api", "description": "添加API", "apigroup": "API", "method": "POST"})).to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status: {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body: {:#?}", json_value);
        assert!(status.is_success());
    }

    #[actix_web::test]
    async fn test_update_api() {
        mysql::init_pool().await;

        super::init_apis().await.expect("init apis error");

        let app = test::init_service(App::new().configure(api::routes)).await;

        let req = test::TestRequest::put().uri("/api").set_json(json!({"id": 32, "path": "/api/api", "description": "删除API", "apigroup": "API", "method": "DELETE"})).to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status: {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body: {:#?}", json_value);
        assert!(status.is_success());
    }

    #[actix_web::test]
    async fn test_delete_api() {
        mysql::init_pool().await;

        super::init_apis().await.expect("init apis error");

        let app = test::init_service(App::new().configure(api::routes)).await;

        let req = test::TestRequest::delete()
            .uri("/api")
            .set_json(json!({"id": 33}))
            .to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status: {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body: {:#?}", json_value);
        assert!(status.is_success());
    }
}
